package xyz.syyrjx.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.*;
import xyz.syyrjx.request.SyyrjxRpcRequest;
import xyz.syyrjx.client.exception.SyyrjxRpcClientException;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 大地崩坏苍蝇兽
 * @date 2023/1/6 - 17:37
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class SyyrjxRpcClient {

    private Channel channel;

    private String remoteHost;

    private int remotePort;

    //最大重试次数
    private static int MAX_RETEST = 5;

    //重试间隔毫秒值
    private static int RETEST_INTERVAL = 300;

    public void run(ChannelInitializer<SocketChannel> channelInitializer) throws InterruptedException {
        if (channel != null) {
            return;
        }
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup loopGroup = new NioEventLoopGroup();

        bootstrap.group(loopGroup)
                .channel(NioSocketChannel.class)
                .handler(channelInitializer);
        ChannelFuture future = bootstrap.connect(remoteHost, remotePort).sync();
        if (future == null){
            throw new SyyrjxRpcClientException(SyyrjxRpcClientException.CONNECT_ERROR);
        }
        ChannelFuture finalFuture = future;
        future.addListener(listener -> {
            int count = 0;
            while (!finalFuture.isSuccess()) {
                if (count++ >= MAX_RETEST) {
                    throw new SyyrjxRpcClientException(SyyrjxRpcClientException.CONNECT_ERROR);
                }
                TimeUnit.MICROSECONDS.sleep(RETEST_INTERVAL);
            }
        });
        channel = future.channel();
    }


    public void send(SyyrjxRpcRequest request) {
        String requestStr = request.toSerializableString();
        channel.writeAndFlush(requestStr);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SyyrjxRpcClient client = (SyyrjxRpcClient) o;
        return remotePort == client.remotePort &&
                Objects.equals(remoteHost, client.remoteHost);
    }

    @Override
    public int hashCode() {
        return Objects.hash(remoteHost, remotePort);
    }
}
